/* 
 * File:   SocketAsincronico.cpp
 * Author: max
 * 
 * Created on June 1, 2013, 9:07 AM
 */

#include "client_SocketAsincronico.h"
#include "common_Codec.h"
#include "common_MutexScopeLocker.h"
#include <string.h>

using namespace TP;
using namespace TP::Comunicacion;

SocketAsincronico::SocketAsincronico (const string& pIp, const string& pPuerto){
    this->_socket = new Socket(pIp, pPuerto);
    this->_transmitiendo = false;
    this->_clave = "";
    this->_clave_seteada = false;
}

SocketAsincronico::SocketAsincronico (const string& pIp, const string& pPuerto, const int &pTimeout){
    _socket = new Socket(pIp, pPuerto, pTimeout);
    this->_transmitiendo = false;
    this->_clave = string("");
    this->_clave_seteada = false;
}

SocketAsincronico::~SocketAsincronico (){
    
    delete _socket;
    string* aux;
    
    while (!bufferInEmpty())
    {
        aux = bufferInPop();
        delete aux;
    }

    while (!bufferOutEmpty())
    {
        aux = bufferOutPop();
        delete aux;
    }
}

void SocketAsincronico::Iniciar() throw (Excepcion&){
    try
    {
        SetTransmitiendo(true);
        _socket->Iniciar();
        Thread::Iniciar();
    }
    catch (Excepcion &ex)
    {
        this->SetTransmitiendo(false);
        throw;
    }
}

void SocketAsincronico::Finalizar() throw (Excepcion&){
    try
    {
        SetTransmitiendo(false);
        _socket->Finalizar();
        
        Thread::Finalizar();
    }
    catch (Excepcion &ex)
    {
        throw;
    }
}

int SocketAsincronico::Escribir(const uint8_t* pMensaje, const size_t pLongitud) throw (Excepcion&){
    
    if (!GetTransmitiendo())
    {
        throw Excepcion ("El socket no se encuentra transmitiendo.");
    }
    
    if (NULL != pMensaje && pLongitud > 0)
    {
        string *mensaje = new string();
        Codec codec;
        codec.codificar(*mensaje, pMensaje, pLongitud);

        bufferInPush(mensaje);
    }
}

int SocketAsincronico::Leer (uint8_t* &pMensaje, uint32_t &pLongitud) throw (Excepcion&){
    
    if (!this->_transmitiendo)
    {
        throw Excepcion ("El socket no se encuentra transmitiendo.");
    }
    
    if (!bufferOutEmpty())
    {
        // Se extrae el primer mensaje de la cola. Se lo decodifica y se lo ubica en pMensaje.
        Codec codec;
        string* aux = bufferOutPop();

        codec.decodificar(pMensaje, pLongitud, *aux);
        delete aux;
    }
    else
    {
        pMensaje = NULL;
        pLongitud = 0;
    }
}

void SocketAsincronico::tarea (){
    
    while (GetTransmitiendo())
    {
        try
        {
            // Se toma un mensaje de la cola de mensaje a enviar y se lo envia por el socket.
            if (!bufferInEmpty())
            {
                string* aux = bufferInPop();
                _socket->Escribir(*aux);
                delete aux;
            }
            else
            {
                _socket->Escribir("KAKAKA");
            }

            // Se lee un menesaje desde el socket y se lo encola en la cola de mensajes saliente.
            string* mensaje = new string();
            _socket->Leer(*mensaje);
            if (strcmp ("KAKAKA",mensaje->c_str()) != 0)
            {
                bufferOutPush(mensaje);
            }
            else
            {
                int chan = 1;
            }
        }
        catch (Excepcion &ex)
        {
            cerr << ex.what() << endl;
            SetTransmitiendo(false);
        }
    }
}

bool SocketAsincronico::GetTransmitiendo(){
    MutexScopeLocker _locker (_mutexTransmitiendo);
    return _transmitiendo;
}

bool SocketAsincronico::SetTransmitiendo(bool pValor){
    MutexScopeLocker _locker (_mutexTransmitiendo);
    _transmitiendo = pValor;
    return _transmitiendo;
}

bool SocketAsincronico::bufferInEmpty(){
    MutexScopeLocker locker (_mutexBufferIn);
    return _bufferIn.empty();
}

string* SocketAsincronico::bufferInPop(){
    MutexScopeLocker locker (_mutexBufferIn);
    string* value = _bufferIn.front();
    _bufferIn.pop();
    return value;
}

string* SocketAsincronico::bufferInPush(string  *pValor){
    MutexScopeLocker locker (_mutexBufferIn);
    _bufferIn.push(pValor);
    return pValor;
}

bool SocketAsincronico::bufferOutEmpty(){
    MutexScopeLocker locker (_mutexBufferOut);
    return _bufferOut.empty();
}

string* SocketAsincronico::bufferOutPop(){
    MutexScopeLocker locker (_mutexBufferOut);
    string* valor = _bufferOut.front();
    _bufferOut.pop();
    return valor;
}

string* SocketAsincronico::bufferOutPush(string *pValor){
    MutexScopeLocker locker (_mutexBufferOut);
    _bufferOut.push(pValor);
    return pValor;
}

void SocketAsincronico::SetClaveFirma(const std::string& clave)
{
    this->_clave = clave;
    this->_clave_seteada = true;
}

std::string SocketAsincronico::GetClaveFirma() const
{
    return this->_clave;
}

bool SocketAsincronico::ClaveSeteada() const
{
    return this->_clave_seteada;
}